home *** CD-ROM | disk | FTP | other *** search
/ SGI Freeware 1999 August / SGI Freeware 1999 August.iso / dist / fw_xemacs.idb / usr / freeware / lib / xemacs-20.4 / lisp / hm--html-menus / internal-drag-and-drop.el.z / internal-drag-and-drop.el
Encoding:
Text File  |  1998-05-21  |  35.2 KB  |  942 lines

  1. ;;; internal-drag-and-drop.el --- Internal drag and drop interface
  2.  
  3. ;; Copyright (C) 1996, 1997 Heiko Muenkel
  4.  
  5. ;; Author: Heiko Muenkel <muenkel@tnt.uni-hannover.de>
  6. ;; Keywords: mouse
  7.  
  8. ;; $Id: internal-drag-and-drop.el,v 1.9 1997/07/20 06:36:20 muenkel Exp $
  9.  
  10. ;; This file is part of XEmacs.
  11.  
  12. ;; XEmacs is free software; you can redistribute it and/or modify it
  13. ;; under the terms of the GNU General Public License as published by
  14. ;; the Free Software Foundation; either version 2, or (at your
  15. ;; option) any later version.
  16.  
  17. ;; XEmacs is distributed in the hope that it will be useful, but
  18. ;; WITHOUT ANY WARRANTY; without even the implied warranty of
  19. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  20. ;; General Public License for more details.
  21.  
  22. ;; You should have received a copy of the GNU General Public License
  23. ;; along with XEmacs; See the file COPYING.  if not, write to the Free
  24. ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
  25. ;; 02111-1307, USA.
  26.  
  27. ;;; Synched up with:  Not part of Emacs.
  28.  
  29. ;;; Commentary:
  30.  
  31. ;; Description:
  32.  
  33. ;;    This package provides functions to define and call internal
  34. ;;    drag and drop actions in the emacs. One could start such an 
  35. ;;    action by clicking with the mouse in the source buffer and 
  36. ;;    then in the destination buffer. The action could depend on
  37. ;;    the points where you've clicked with the mouse, on the state
  38. ;;    of the region, the point, the mark and any other properties
  39. ;;    of the source and the destination buffers. The actions are
  40. ;;    defined by the variable `idd-actions', which is a buffer local
  41. ;;    variable. The following is an example for the hm--html-mode:
  42. ;;    (defvar hm--html-idd-actions
  43. ;;      '((nil (((idd-if-major-mode-p . dired-mode)
  44. ;;           (idd-if-dired-file-on-line-p 
  45. ;;                . ".*\\.\\(gif\\)\\|\\(jpq\\)"))
  46. ;;          hm--html-idd-add-include-image-from-dired-line)
  47. ;;         (((idd-if-major-mode-p . dired-mode)
  48. ;;           (idd-if-dired-no-file-on-line-p . nil))
  49. ;;          hm--html-idd-add-file-link-to-file-on-dired-line)
  50. ;;         (((idd-if-major-mode-p . dired-mode)
  51. ;;           (idd-if-dired-no-file-on-line-p . t))
  52. ;;          hm--html-idd-add-file-link-to-directory-of-buffer)
  53. ;;         (((idd-if-major-mode-p . w3-mode)
  54. ;;           (idd-if-url-at-point-p . t))
  55. ;;          hm--html-idd-add-html-link-from-w3-buffer-point)
  56. ;;         (((idd-if-major-mode-p . w3-mode))
  57. ;;          hm--html-idd-add-html-link-to-w3-buffer)
  58. ;;         (((idd-if-local-file-p . t))
  59. ;;          hm--html-idd-add-file-link-to-buffer)))
  60. ;;    Look at the variable `idd-actions' for further descriptions.
  61.  
  62.  
  63. ;; Installation: 
  64.  
  65. ;;    Put this file in one of your load path directories.
  66.  
  67. ;;    Put the following in your .emacs:
  68. ;;    (autoload 'idd-mouse-drag-and-drop "internal-drag-and-drop"
  69. ;;       "Performs a drag and drop action.
  70. ;;         At first you must click on the source and 
  71. ;;         after that on the destination."
  72. ;;       t)
  73.  
  74. ;;    Define actions in the variable `idd-actions'.
  75.  
  76. ;;    The variable `idd-global-mouse-keys' defines the mouse keys,
  77. ;;    which are bound to the drag and drop command.
  78.  
  79. ;;    The variable `idd-global-help-mouse-keys' defines the mouse keys,
  80. ;;    which are bound to the drag and drop help command.
  81.  
  82. ;;    The variable `idd-drag-and-drop-mouse-binding-type' determines
  83. ;;    if you've to hold a mouse button down during moving the mouse
  84. ;;    from the source to the destination or not.
  85.  
  86. ;;    Emacs 19 users should read carefully the whole comments of
  87. ;;    `idd-drag-and-drop-mouse-binding-type', `idd-global-mouse-keys'
  88. ;;    and `idd-global-help-mouse-keys', if they would like to change
  89. ;;    any of these variables or the mouse bindings!
  90.  
  91. ;;; Code:
  92.  
  93. (require 'adapt)
  94. (require 'cl)
  95.  
  96. (defgroup idd-drag-and-drop nil
  97.   "This package provides functions to define and call internal
  98. drag and drop actions in the emacs. One could start such an 
  99. action by clicking with the mouse in the source buffer and 
  100. then in the destination buffer. The action could depend on
  101. the points where you've clicked with the mouse, on the state
  102. of the region, the point, the mark and any other properties
  103. of the source and the destination buffers. The actions are
  104. defined by the variable `idd-actions', which is a buffer local
  105. variable."
  106.   :group 'mouse)
  107.  
  108. (defcustom idd-drag-and-drop-mouse-binding-type 'click
  109.   "*The type of the drag and drop mouse binding.
  110. The value maybe `click' or `press-button-during-move'.
  111. A value of `click' means, that you've to click over the source, leave
  112. the button and click it again over the destination.
  113. A value of `press-button-during-move' means, that you've to press
  114. the button down over the source and hold it until the mouse pointer
  115. is over the destination.
  116.  
  117. The disadvantage of the `press-button-during-move' type compared with 
  118. the `click' type is, that you can't select a destination region and 
  119. therefore a drag and drop action depending on a selected region can't
  120. be started with that type of mouse binding.
  121.  
  122. Note: In the Emacs 19 you'll have to change also the keybindings of
  123. the drag and drop commands, if you change this variable. Look at the
  124. variables `idd-global-mouse-keys' and `idd-global-help-mouse-keys' for
  125. this."
  126.   :group 'idd-drag-and-drop
  127.   :type '(choice (const :tag "Click on source and destination"
  128.             :value click)
  129.          (const :tag "Press button during mouse move"
  130.             :value press-button-during-move)))
  131.  
  132. (defvar idd-global-mouse-keys (if (adapt-emacs19p)
  133.                   (if (eq idd-drag-and-drop-mouse-binding-type
  134.                       'click)
  135.                       [(meta control mouse-1)]
  136.                     [(meta control down-mouse-1)])
  137.                 [(meta control button1)])
  138.   "The mouse keys for the command `idd-mouse-drag-and-drop'.
  139. The command `idd-mouse-drag-and-drop' is bound during the loading
  140. of the package internal-drag-and-drop to this keys in the global
  141. key map. 
  142.  
  143. Set it to nil, if you don't want to bind this function during loading.
  144.  
  145. If the command is already bound in the global keymap during loading,
  146. then this key sequence will not be bind.
  147.  
  148. Note: In the Emacs 19 the mouse keys must contain the modifier
  149. `down', if `idd-drag-and-drop-mouse-binding-type' is set to 
  150. `press-button-during-move' and must not contain the modifier, if it
  151. is set to `click'. If you set `idd-drag-and-drop-mouse-binding-type'
  152. before loading the package internal-drag-and-drop, the mouse will
  153. be bind in the right way.")
  154.  
  155. (defvar idd-global-help-mouse-keys
  156.   (if (adapt-emacs19p)
  157.       (if (eq idd-drag-and-drop-mouse-binding-type 'click)
  158.       [(meta control mouse-3)]
  159.     [(meta control down-mouse-3)])
  160.     [(meta control button3)])
  161.   "The mouse keys for the command `idd-help-mouse-drag-and-drop'.
  162. The command `idd-help-mouse-drag-and-drop' is bound during the loading
  163. of the package internal-drag-and-drop to this keys in the global
  164. key map. 
  165.  
  166. Set it to nil, if you don't want to bind this function during loading.
  167.  
  168. If the command is already bound in the global keymap during loading,
  169. then this key sequence will not be bind.
  170.  
  171. Note: In the Emacs 19 the mouse keys must contain the modifier
  172. `down', if `idd-drag-and-drop-mouse-binding-type' is set to 
  173. `press-button-during-move' and must not contain the modifier, if it
  174. is set to `click'. If you set `idd-drag-and-drop-mouse-binding-type'
  175. before loading the package internal-drag-and-drop, the mouse will
  176. be bind in the right way.")
  177.  
  178. (defcustom idd-actions '((((idd-if-region-active-p . nil))
  179.               (((idd-if-region-active-p . t))
  180.                idd-action-copy-region))
  181.              
  182.              (((idd-if-region-active-p . t))
  183.               (((idd-if-region-active-p . t))
  184.                idd-action-copy-replace-region))
  185.              
  186.              (((idd-if-region-active-p . nil)
  187.                (idd-if-modifiers-p . nil))
  188.               (((idd-if-region-active-p . t))
  189.                idd-action-move-region))
  190.              
  191.              (((idd-if-region-active-p . t)
  192.                (idd-if-modifiers-p . nil))
  193.               (((idd-if-region-active-p . t))
  194.                idd-action-move-replace-region))
  195.              )
  196.   "*The list with actions, depending on the source and the destination.
  197. The list looks like:
  198.   '((<destination-specification-1> (<source-specification-1> <action-1-1>)
  199.                                    (<source-specification-2> <action-1-2>)
  200.                                    :
  201.      )
  202.     (<destination-specification-2> (<source-specification-1> <action-2-1>)
  203.                                    (<source-specification-2> <action-2-2>)
  204.                                     :
  205.      )
  206.     :
  207.     )
  208. The <source-specification> looks like the following:
  209.   '([(<specification-type> <value>)])
  210. with <specification-type> :== idd-if-minor-mode-p | idd-if-buffer-name-p
  211.                   | idd-if-region-active-p | idd-if-url-at-point-p
  212.                               | idd-if-major-mode-p | idd-if-variable-non-nil-p
  213.                               | idd-if-dired-file-on-line-p
  214.                               | idd-if-dired-no-file-on-line-p
  215.                               | idd-if-local-file-p | idd-if-buffer-name-p
  216.                               | idd-if-modifiers-p | ...
  217.  
  218. The <specification-type> - functions must have two arguments, the first one
  219. is the source or destination and the second is the <value>. It must return
  220. nil, if the test wasn't successfull and a number (in general 1), which 
  221. specifies the weight of the test function. The weights of all single tests
  222. are added to a summary weight and assigned to the action. The action
  223. with the highest weight is called from the action handler. Look at
  224. the definition of `idd-if-major-mode-p', `idd-if-minor-mode-p' and so on for
  225. examples. Look at the function `idd-get-source-or-destination-alist', if
  226. you wan't to know the structure of the 'source-or-destination' argument
  227. of these functions.
  228.  
  229. The <destination-specification> looks like <source-specification>,
  230. but in general it could be set to nil in mode specific idd-action
  231. lists.
  232.  
  233. If <destination-specification-1> or <source-specification-1> is set to
  234. nil, then every source or source matches. `idd-actions' is a
  235. buffer local variable, which should be at least mode depended. So if
  236. the <destination-specification-1> is set to nil it says, that the destination
  237. buffer must only have a specific mode. But however, it's also possible
  238. to define a general `idd-actions' list, where the destination mode is
  239. specified by `idd-if-major-mode-p'.
  240.  
  241. <action> ist a function, which has two arguments, the first specifies the
  242. source and the second the destination. Look at the function definition
  243. of `idd-action-copy-region' and `idd-action-copy-replace-region'. They are 
  244. examples for such actions."
  245.   :group 'idd-drag-and-drop
  246.   :type 'list)
  247.  
  248. (make-variable-buffer-local 'idd-actions)
  249.  
  250. (defcustom idd-help-instead-of-action nil
  251.   "*If this variable is t, then a help buffer is displayed.
  252. No action will be performed if this variable is t.
  253.  
  254. Note: You can also use the help mouse key instead of setting
  255. this to t."
  256.   :group 'idd-drag-and-drop
  257.   :type 'boolean)
  258.  
  259. (defcustom idd-mouse-pointer-image "drop"
  260.   "*The name of the image used as mouse pointer during drag and drop.
  261. The image must be in the directory `idd-data-directory'.
  262. Run the command `idd-make-drag-and-drop-pointer-glyph' after changing
  263. this variable."
  264.   :group 'idd-drag-and-drop
  265.   :type 'file)
  266.  
  267. (defcustom idd-data-directory (locate-data-directory "idd")
  268.   "Data directory for the file `idd-mouse-pointer-image'.
  269. Run the command `idd-make-drag-and-drop-pointer-glyph' after changing
  270. this variable."
  271.   :group 'idd-drag-and-drop
  272.   :type 'directory)
  273.  
  274. (defcustom idd-overwrite-mouse-pointers 
  275.   (if (adapt-xemacsp) 
  276.       '(text-pointer-glyph
  277.     nontext-pointer-glyph
  278.     selection-pointer-glyph)
  279.     nil)
  280.   "*A list with pointer glyph variables, which should be overwritten
  281. by the idd-drag-and-drop-pointer-glyph. If it is nil, the pointer
  282. wont be changed. Currently it must be nil in the Emacs."
  283.   :group 'idd-drag-and-drop
  284.   :type '(repeat lisp))
  285.  
  286. (defvar idd-drag-and-drop-pointer-glyph nil
  287. ;  (if idd-overwrite-mouse-pointers
  288. ;      (make-pointer-glyph 
  289. ;       (vector 'autodetect :data idd-mouse-pointer-image))
  290. ;    nil)
  291.   "The shape of the mouse-pointer when internal drag and drop is active.")
  292.  
  293. (defvar idd-original-pointer-image-instances nil
  294.   "Internal variable. Alist with the saved images instances of the pointers.
  295. This list is used to restore the old mouse pointers.")
  296.  
  297. (defvar idd-help-start-action-keymap nil
  298.   "Keymap used in an extent in the help buffer to start the action.")
  299.  
  300. (defvar idd-help-source nil
  301.   "Contains the source of an action. Used only in the help buffer.")
  302.  
  303. (defvar idd-help-destination nil
  304.   "Contains the destination of an action. Used only in the help buffer.")
  305.  
  306. (defvar idd-help-start-extent nil
  307.   "The start extent in the help buffer.")
  308.  
  309. (defun idd-compare-a-specification (source-or-destination
  310.                     specification)
  311.   "Tests if SOURCE-OR-DESTINATION matches the SPECIFICATION.
  312. It returns a value (1 in general) if both are matching or nil."
  313.   (eval (list (car specification)
  314.           'source-or-destination
  315.           '(cdr specification))))
  316.  
  317. (defun idd-get-old-pointer-image-instances (mouse-pointers)
  318.   "Returns an alist with the pointer variables and there image instances."
  319.   (cond ((not mouse-pointers) nil)
  320.     (t (cons (cons (car mouse-pointers)
  321.                (glyph-image-instance (eval (car mouse-pointers))))
  322.          (idd-get-old-pointer-image-instances (cdr mouse-pointers))))))
  323.  
  324. (defun idd-set-drag-and-drop-pointer-glyphs-1 (mouse-pointers
  325.                            drag-and-drop-pointer-glyph)
  326.   "Internal function."
  327.   (cond ((not mouse-pointers))
  328.     (t (set-glyph-image (eval (car mouse-pointers))
  329.                 (glyph-image-instance drag-and-drop-pointer-glyph))
  330.        (idd-set-drag-and-drop-pointer-glyphs-1 (cdr mouse-pointers)
  331.                            drag-and-drop-pointer-glyph)
  332.        )))
  333.  
  334. (defun idd-set-drag-and-drop-pointer-glyphs ()
  335.   "Set the shape of some pointers to the drag and drop shape.
  336. Only the pointers in the list `idd-overwrite-mouse-pointers' are
  337. used."
  338.   (unless (or idd-original-pointer-image-instances
  339.           (not idd-overwrite-mouse-pointers))
  340.     (setq idd-original-pointer-image-instances
  341.       (idd-get-old-pointer-image-instances idd-overwrite-mouse-pointers))
  342.     (idd-set-drag-and-drop-pointer-glyphs-1 idd-overwrite-mouse-pointers
  343.                         idd-drag-and-drop-pointer-glyph)))
  344.  
  345. (defun idd-restore-original-pointer-glyphs-1 (pointer-alist)
  346.   "Internal function."
  347.   (cond ((not pointer-alist))
  348.     (t (set-glyph-image (eval (car (car pointer-alist)))
  349.                 (cdr (car pointer-alist)))
  350.        (idd-restore-original-pointer-glyphs-1 (cdr pointer-alist)))))
  351.  
  352. (defun idd-restore-original-pointer-glyphs ()
  353.   "Restores the original pointer shapes."
  354.   (interactive)
  355.   (when idd-overwrite-mouse-pointers
  356.     (idd-restore-original-pointer-glyphs-1
  357.      idd-original-pointer-image-instances)
  358.     (setq idd-original-pointer-image-instances nil)))
  359.  
  360. (defun idd-compare-specifications-1 (source-or-destination
  361.                      specifications
  362.                      value)
  363.   "Internal function of `idd-compare-specifications'.
  364. VALUE is the value of the last matches."
  365.   (cond ((not specifications) value)
  366.     (t (let ((match (idd-compare-a-specification source-or-destination
  367.                              (car specifications))))
  368.          (cond ((not match) 0)
  369.            (t (idd-compare-specifications-1 source-or-destination
  370.                             (cdr specifications)
  371.                             (+ value match))))))))
  372.  
  373. (defun idd-compare-specifications (source-or-destination
  374.                    specifications)
  375.   "Determines how good SOURCE-OR-DESTINATION and SPECIFICATIONS are matching.
  376. A return value of zero means, that they don't match. The higher the
  377. return value the better is the matching."
  378.   (cond ((not specifications) 1)
  379.     (t (idd-compare-specifications-1 source-or-destination
  380.                      specifications
  381.                      0))))
  382.  
  383. (defun idd-get-action-depending-on-source (source
  384.                        actions-depending-on-source
  385.                        destination-value
  386.                        value-action-pair)
  387.   "Internal function of `idd-get-action-depending-on-source-and-destination'."
  388.   (let ((source-value (idd-compare-specifications
  389.                source
  390.                (car (car actions-depending-on-source)))))
  391.     (cond ((not actions-depending-on-source) value-action-pair)
  392.       ((or (= source-value 0)
  393.            (<= (+ destination-value source-value) (car value-action-pair)))
  394.        (idd-get-action-depending-on-source 
  395.         source
  396.         (cdr actions-depending-on-source)
  397.         destination-value
  398.         value-action-pair))
  399.       (t (idd-get-action-depending-on-source 
  400.           source
  401.           (cdr actions-depending-on-source)
  402.           destination-value
  403.           (cons (+ destination-value source-value)
  404.             (second (car actions-depending-on-source))))))))
  405.  
  406. (defun idd-get-action-depending-on-source-and-destination (source
  407.                                destination
  408.                                actions
  409.                                value-action-pair)
  410.   "Internal function of `idd-get-action'.
  411. VALUE-ACTION-PAIR is a list like (<value> <action>).
  412. It returns VALUE-ACTION-PAIR, if no other action is found, which has a
  413. value higher than (car VALUE-ACTION-PAIR)."
  414.   (let ((destination-value
  415.      (idd-compare-specifications destination (car (car actions)))))
  416.     (cond ((not actions) value-action-pair)
  417.       ((= destination-value 0)
  418.        (idd-get-action-depending-on-source-and-destination
  419.         source
  420.         destination
  421.         (cdr actions)
  422.         value-action-pair))
  423.       (t (idd-get-action-depending-on-source-and-destination
  424.           source
  425.           destination
  426.           (cdr actions)
  427.           (idd-get-action-depending-on-source
  428.            source
  429.            (cdr (car actions))
  430.            destination-value
  431.            value-action-pair))))))
  432.  
  433. (defun idd-get-action (source destination actions)
  434.   "Returns the action, which depends on the SOURCE and the DESTINATION.
  435. The list ACTIONS contains all possible actions. Look at the variable
  436. `idd-actions' for a description of the format of this list."
  437.   (idd-get-action-depending-on-source-and-destination source
  438.                               destination
  439.                               actions
  440.                               '(0 . nil)))
  441.  
  442. ;(autoload 'ange-ftp-ftp-path "ange-ftp"
  443. ;  "Parse PATH according to ange-ftp-path-format (which see).
  444. ;Returns a list (HOST USER PATH), or nil if PATH does not match the format.")
  445.  
  446. (defun idd-get-buffer (source-or-destination)
  447.   "Returns the buffer of the SOURCE-OR-DESTINATION."
  448.   (cdr (assoc ':buffer source-or-destination)))
  449.  
  450. (defun idd-set-point (source-or-destination)
  451.   "Sets the point and buffer to SOURCE-OR-DESTINATION."
  452.   (set-buffer (idd-get-buffer source-or-destination))
  453.   (goto-char (cdr (assoc ':drag-or-drop-point source-or-destination))))
  454.  
  455. (defun idd-set-region (source-or-destination)
  456.   "Sets the point, mark and buffer to SOURCE-OR-DESTINATION.
  457. The region is active after this function is called."
  458.   (set-buffer (idd-get-buffer source-or-destination))
  459.   (goto-char (car (cdr (assoc ':region-active source-or-destination))))
  460.   (set-mark (cdr (cdr (assoc ':region-active source-or-destination))))
  461.   (if (adapt-xemacsp)
  462.       (activate-region))
  463.   )
  464.  
  465.  
  466. ;;; Specification type functions for the list `idd-actions'
  467.  
  468. (defun idd-if-region-active-p (source-or-destination value)
  469.   "Checks if the region in the SOURCE-OR-DESTINATION was active.
  470. It returns 1, if the region was active and VALUE is t, or if
  471. the region was not active and VALUE is nil. Otherwise it returns
  472. nil."
  473.   (if (cdr (assoc ':region-active source-or-destination))
  474.       (if value 1 nil)
  475.     (if value nil 1)))
  476.  
  477. (defun idd-get-buffer-url (source-or-destination)
  478.   "Returns the URL of the buffer specified by SOURCE-OR-DESTINATION."
  479.   (save-excursion
  480.     (idd-set-point source-or-destination)
  481.     (url-view-url t)))
  482.  
  483. (defun idd-get-url-at-point (source-or-destination)
  484.   "Returns the URL at the point specified by SOURCE-OR-DESTINATION.
  485. It returns nil, if there is no URL."
  486.   (save-excursion
  487.     (idd-set-point source-or-destination)
  488.     (w3-view-this-url t)))
  489.  
  490. (defun idd-if-url-at-point-p (source-or-destination value)
  491.   "Checks if there is an URL at the point of SOURCE-OR-DESTINATION.
  492. If that is t and VALUE is t, or that is nil and VALUE is nil, then 1
  493. is returned. Otherwise nil is returned."
  494.   (if value
  495.       (if (idd-get-url-at-point source-or-destination)
  496.       1
  497.     nil)
  498.     (if (idd-get-url-at-point source-or-destination)
  499.     nil
  500.       1)))
  501.  
  502. (defun idd-if-major-mode-p (source-or-destination mode)
  503.   "Checks, if the major mode of SOURCE-OR-DESTINATION is MODE.
  504. It returns 1, if that is t and nil otherwise."
  505.   (save-excursion
  506.     (set-buffer (idd-get-buffer source-or-destination))
  507.     (if (eq major-mode mode)
  508.     1
  509.       nil)))
  510.  
  511. (defun idd-if-variable-non-nil-p (source-or-destination variable)
  512.   "Checks, if the variable named VARIABLE isn't t in  SOURCE-OR-DESTINATION.
  513. It returns 1, if this is t."
  514.   (save-excursion
  515.     (set-buffer (idd-get-buffer source-or-destination))
  516.     (if (eval variable)
  517.     1
  518.       nil)))
  519.  
  520. (defun idd-if-minor-mode-p (source-or-destination minor-mode-variable)
  521.   "Checks, if the variable MINOR-MODE-VARIABLE is t in SOURCE-OR-DESTINATION.
  522. MINOR-MODE-VARIABLE is the name of the variable!."
  523.   (idd-if-variable-non-nil-p source-or-destination minor-mode-variable))
  524.  
  525. (defun idd-get-dired-filename-from-line (source-or-destination)
  526.   "Returns the filename form the line in a dired buffer.
  527. The position and the buffer is specified by SOURCE-OR-DESTINATION."
  528.   (save-excursion
  529.     (idd-set-point source-or-destination)
  530.     (dired-get-filename nil t)))
  531.  
  532. (defun idd-if-dired-file-on-line-p (source-or-destination filename-regexp)
  533.   "Checks, if the filename on the line match FILENAME-REGEXP.
  534. The function `dired-get-filename' is used, to get the filename from
  535. the SOURCE-OR-DESTINATION. It returns 1, if it matchs or nil."
  536.   (let ((case-fold-search t))
  537.     (if (and (idd-get-dired-filename-from-line source-or-destination)
  538.          (string-match filename-regexp
  539.                (idd-get-dired-filename-from-line
  540.                 source-or-destination)))
  541.     1
  542.       nil)))
  543.            
  544. (defun idd-if-dired-no-file-on-line-p (source-or-destination value)
  545.   "Checks, if a filename is in the dired buffer of SOURCE-OR-DESTINATION.
  546. It returns 1, if a filename is on the line and if VALUE is t, or if
  547. no filename is on the line and VALUE is nil, otherwise it returns
  548. nil. For the test the function `dired-get-filename' is used."
  549.   (if (idd-get-dired-filename-from-line source-or-destination)
  550.       (if value nil 1)
  551.     (if value 1 nil)))
  552.  
  553. (defun idd-get-local-filename (source-or-destination)
  554.   "Returns the filename of a local file specified by SOURCE-OR-DESTINATION."
  555.   (buffer-file-name (idd-get-buffer source-or-destination)))
  556.  
  557. (defun idd-get-directory-of-buffer (source-or-destination)
  558.   "Returns the directory name assigned to the SOURCE-OR-DESTINATION buffer."
  559.   (save-excursion
  560.     (idd-set-point source-or-destination)
  561.     default-directory))
  562.  
  563. (defun idd-if-local-file-p (source-or-destination value)
  564.   "Checks, if SOURCE-OR-DESTINATION has a file on the local filesystem.
  565. If that is t and VALUE is t, or that is nil and VALUE is nil, then 1
  566. is returned. Otherwise nil is returned."
  567.   (let ((filename (idd-get-local-filename source-or-destination)))
  568.     (if (and filename
  569. ;         (not (ange-ftp-ftp-path filename)))
  570.          (not (file-remote-p filename)))
  571.     (if value 1 nil)
  572.       (if value nil 1))))
  573.  
  574. (defun idd-if-buffer-name-p (source-or-destination buffer-name)
  575.   "Checks, if SOURCE-OR-DESTINATION has a buffer called BUFFER-NAME.
  576. It returns 1 if this is the case or nil otherwise."
  577.   (if (string= buffer-name
  578.            (buffer-name (idd-get-buffer source-or-destination)))
  579.       1
  580.     nil))
  581.  
  582. (defun idd-list-1-subset-of-list-2 (list-1 list-2)
  583.   "Returns t, if LIST-1 is a subset of LIST-2."
  584.   (cond ((not list-1))
  585.     ((member (car list-1) list-2)
  586.      (idd-list-1-subset-of-list-2 (cdr list-1) list-2))
  587.     (t nil)))
  588.  
  589. (defun idd-same-elements-p (list-1 list-2)
  590.   "Returns t, if both list have the same modifiers."
  591.   (and (= (length list-1) (length list-2))
  592.        (idd-list-1-subset-of-list-2 list-1 list-2)))
  593.  
  594. (defun idd-if-modifiers-p (source-or-destination modifiers)
  595.   "Checks, if the MODIFIERS hold during selecting the SOURCE-OR-DESTINATION.
  596. Returns 1, if the list MODIFIERS contains the same modifiers,
  597. or if any modyfiers are hold and MODIFIERS is t,
  598. or if no modyfiers are hold and MODIFIERS is nil.
  599. Otherwise nil is returned."
  600.   (let ((event-modifiers (event-modifiers
  601.               (cdr (assoc ':event source-or-destination)))))
  602.     (cond ((not modifiers)
  603.        (if event-modifiers nil 1))
  604.       ((listp modifiers)
  605.        (if (idd-same-elements-p modifiers event-modifiers)
  606.            1
  607.          nil))
  608.       (t (if event-modifiers 1 nil)))))
  609.  
  610. ;;; action functions
  611.  
  612. (defun idd-action-copy-region (source destination)
  613.   "Copy the region from DESTINATION to SOURCE."
  614.   (idd-set-region source)
  615.   (let ((region-contents (buffer-substring (point) (mark))))
  616.     (idd-set-point destination)
  617.     (insert region-contents)))
  618.  
  619. (defun idd-action-copy-replace-region (source destination)
  620.   "Copy the region from SOURCE and replace the DESTINATION region with it."
  621.   (idd-set-region source)
  622.   (let ((region-contents (buffer-substring (point) (mark))))
  623.     (idd-set-region destination)
  624.     (delete-region (point) (mark))
  625.     (insert region-contents)))
  626.  
  627. (defmacro* idd-with-source-and-destination (source
  628.                         destination
  629.                         &key
  630.                         do-in-source
  631.                         do-in-destination)
  632.   "Macro, usefull for the definition of action functions.
  633. Look at the example `idd-action-move-region'."
  634.   `(progn
  635.      (if (idd-if-region-active-p ,source t)
  636.      (idd-set-region ,source)
  637.        (idd-set-point ,source))
  638.      ,(when do-in-source
  639.     (cons 'progn do-in-source))
  640.      (if (idd-if-region-active-p ,destination t)
  641.      (idd-set-region ,destination)
  642.        (idd-set-point ,destination))
  643.      ,(when do-in-destination
  644.     (cons 'progn do-in-destination))))
  645.   
  646. (defun idd-action-move-region (source destination)
  647.   "Move the region from SOURCE to DESTINATION."
  648.   (let ((region))
  649.     (idd-with-source-and-destination 
  650.      source destination
  651.      :do-in-source ((setq region (buffer-substring (point) (mark)))
  652.             (delete-region (point) (mark)))
  653.      :do-in-destination ((insert region)))))
  654.  
  655.  
  656. (defun idd-action-move-replace-region (source destination)
  657.   "Delete the region at SOURCE and overwrite the DESTINATION region with it."
  658.   (let ((region))
  659.     (idd-with-source-and-destination 
  660.      source destination
  661.      :do-in-source ((setq region (buffer-substring (point) (mark)))
  662.             (delete-region (point) (mark)))
  663.      :do-in-destination ((delete-region (point) (mark))
  664.              (insert region)))))
  665.  
  666.  
  667. ;;; Performing the drag and drop
  668.  
  669. (defun idd-display-help-about-action (action source destination)
  670.   "Display a help buffer with information about the action."
  671.   (if (> (car action) 0)
  672.       (if (symbol-function (cdr action))
  673.       (progn
  674.         (with-displaying-help-buffer
  675.          '(lambda ()
  676.         (set-buffer "*Help*")
  677.         (setq idd-help-source source)
  678.         (setq idd-help-destination destination)
  679.         (insert "Drag and drop action: `")
  680.         (let ((start (point)))
  681.           (insert (format "%s" (cdr action)))
  682.           (setq idd-help-start-extent (make-extent start (point)))
  683.           (set-extent-mouse-face idd-help-start-extent 'highlight)
  684.           (set-extent-face idd-help-start-extent 'bold)
  685.           (if (adapt-xemacsp)
  686.               (set-extent-keymap idd-help-start-extent
  687.                      idd-help-start-action-keymap)
  688.             )
  689.           )
  690.         (insert "'\n")
  691.         (insert (format "Source buffer       : `%s'\n"
  692.                 (buffer-name (cdr (assoc ':buffer source)))))
  693.         (insert (format "Destination buffer  : `%s'\n"
  694.                 (buffer-name (cdr (assoc ':buffer destination))
  695.                          )))
  696.         (insert "=================================================="
  697.             "====================\n")
  698.         (insert "Look at `idd-actions' in the "
  699.             "destination buffer for other actions!\n")
  700.         (insert (format "The documentation of `%s':\n\n"
  701.                 (cdr action)))
  702.         (insert (documentation (cdr action)))))
  703.         )
  704.     (error "Error: Action %s isn't a valid function!" (cdr action)))
  705.     (message "No valid action defined for this source and this destination!")))
  706.  
  707. (defun idd-call-action (action source destination)
  708.   "Calls the drag and drop ACTION with its arguments SOURCE and DESTINATION."
  709.   (if (> (car action) 0)
  710.       (if (symbol-function (cdr action))
  711.       (eval (list (cdr action) 'source 'destination))
  712.     (error "Error: Action %s isn't a valid function!" (cdr action)))
  713.     (message "No valid action defined for this source and this destination!")))
  714.  
  715. (defun idd-start-help-mouse-drag-and-drop ()
  716.   "Starts help on `idd-start-mouse-drag-and-drop'."
  717.   (interactive)
  718.   (let ((idd-help-instead-of-action t))
  719.     (idd-start-mouse-drag-and-drop)))
  720.  
  721. (defun idd-start-mouse-drag-and-drop ()
  722.   "Starts a drag and drop command.
  723. This command could be used to start a drag and drop command without a
  724. button event. Therefore this should not be bind direct to a mouse button."
  725.   (interactive)
  726.   (let ((source-event)
  727.     (drag-and-drop-message "Drag&Drop: Click on the source!"))
  728.     (message drag-and-drop-message)
  729.     (setq source-event
  730.       (next-command-event nil drag-and-drop-message))
  731.     (if (button-press-event-p source-event)
  732.     (progn
  733.       (when (and (adapt-emacs19p)
  734.              (mouse-event-p source-event)
  735.              (eq idd-drag-and-drop-mouse-binding-type 'click))
  736.         (while (not (button-release-event-p (next-command-event)))))
  737.       (idd-mouse-drag-and-drop source-event)) 
  738.       (message "Wrong event! Exit drag and drop."))))
  739.  
  740. (defun idd-help-mouse-drag-and-drop (source-event)
  741.   "Displays help about the drag and drop action."
  742.   (interactive "@e")
  743.   (let ((idd-help-instead-of-action t))
  744.     (idd-mouse-drag-and-drop source-event)))
  745.  
  746. (defun idd-mouse-drag-and-drop (source-event)
  747.   "Performs a drag and drop action.
  748. It calls the command `idd-mouse-drag-and-drop-click' or 
  749. `idd-mouse-drag-and-drop-press-button-during-move' depending on
  750. the value of `idd-drag-and-drop-mouse-binding-type'."
  751.   (interactive "@e")
  752.   (if (eq idd-drag-and-drop-mouse-binding-type 'click)
  753.       (idd-mouse-drag-and-drop-click source-event)
  754.     (idd-mouse-drag-and-drop-press-button-during-move source-event)))
  755.  
  756. (defun idd-get-source-or-destination-alist (event)
  757.   "Returns an alist with the description of a source or destination point.
  758. The EVENT must be the button event, which has selected the source or
  759. destination of the drag and drop command.
  760.  
  761. The alist has the following structure:
  762.    '((:buffer . <buffer-of-the-event>)
  763.      (:drag-or-drop-point . <closest-point-to-the-event>)
  764.      (:region-active . <t-or-nil>)
  765.      (:event . EVENT))
  766.  
  767. Note: <closest-point-to-the-event> is (event-closest-point EVENT),
  768. if the EVENT is a mouse event and if it isn't nil. Otherwise the
  769. point is used."
  770. ;  (set-buffer (event-buffer event))
  771.   (list (cons ':buffer (event-buffer event))
  772.     (cons ':drag-or-drop-point (set-marker
  773.                     (make-marker)
  774.                     (if (mouse-event-p event)
  775.                     (or (event-closest-point event)
  776.                         (point))
  777.                       (point))))
  778.     (cons ':region-active (if (region-active-p)
  779.                   (cons (set-marker (make-marker) (point))
  780.                     (set-marker (make-marker) (mark)))))
  781.     (cons ':event event))
  782.   )
  783.  
  784. (defun idd-mouse-drag-and-drop-press-button-during-move (source-event)
  785.   "Performs a drag and drop action.
  786. At first you must press the button down over the source and then
  787. move with the pressed button to the destination, where you must leave
  788. the button up.
  789. This must be bind to a mouse button. The SOURCE-EVENT must be a 
  790. button-press-event.
  791.  
  792. The disadvantage of this command compared with the command
  793. `idd-mouse-drag-and-drop-click' is, that you can't select a
  794. destination region."
  795.   (interactive "@e")
  796.   (idd-set-drag-and-drop-pointer-glyphs)
  797.   (let ((drag-and-drop-message 
  798.      "Drag&Drop: Leave the button over the destination!")
  799.     (source (idd-get-source-or-destination-alist source-event))
  800.     (destination nil)
  801.     (destination-event))
  802.     (message drag-and-drop-message)
  803.     (setq destination-event 
  804.       (next-command-event nil drag-and-drop-message))
  805.     (message "")
  806.     (cond ((or (button-release-event-p destination-event)
  807.            (and (adapt-emacs19p)
  808.             (button-drag-event-p destination-event)))
  809.        (setq destination (idd-get-source-or-destination-alist
  810.                   destination-event))
  811.        (idd-set-point destination)
  812.        (if idd-help-instead-of-action
  813.            (idd-display-help-about-action (idd-get-action source
  814.                                   destination
  815.                                   idd-actions)
  816.                           source
  817.                           destination)
  818.          (idd-call-action (idd-get-action source destination idd-actions)
  819.                   source
  820.                   destination)))
  821.       (t (message "Wrong event! Exit drag and drop.") nil)))
  822.   (idd-restore-original-pointer-glyphs))
  823.  
  824. (defun idd-mouse-drag-and-drop-click (source-event)
  825.   "Performs a drag and drop action.
  826. At first you must click on the source and after that on the destination.
  827. This must be bind to a mouse button. The SOURCE-EVENT must be a 
  828. button-press-event."
  829.   (interactive "@e")
  830.   (idd-set-drag-and-drop-pointer-glyphs)
  831.   (let ((drag-and-drop-message "Drag&Drop: Click on the destination!")
  832.     (source (idd-get-source-or-destination-alist source-event))
  833.     (destination nil)
  834.     (destination-event))
  835.     (message drag-and-drop-message)
  836.     (when (and (adapt-xemacsp) (mouse-event-p source-event))
  837.       (dispatch-event (next-command-event)))
  838.     (setq destination-event 
  839.       (next-command-event nil drag-and-drop-message))
  840.     (message "")
  841.     (cond ((button-press-event-p destination-event)
  842.        (mouse-track destination-event)
  843.        (setq destination (idd-get-source-or-destination-alist
  844.                   destination-event))
  845.        (idd-set-point destination)
  846. ;       (when (adapt-emacs19p)
  847. ;           (while (not (button-release-event-p (next-command-event)))))
  848.        (if idd-help-instead-of-action
  849.            (idd-display-help-about-action (idd-get-action source
  850.                                   destination
  851.                                   idd-actions)
  852.                           source
  853.                           destination)
  854.          (idd-call-action (idd-get-action source destination idd-actions)
  855.                   source
  856.                   destination)))
  857.       ((and (adapt-emacs19p)
  858.         (button-click-event-p destination-event))
  859.        (setq destination (idd-get-source-or-destination-alist
  860.                   destination-event))
  861.        (idd-set-point destination)
  862.        (if idd-help-instead-of-action
  863.            (idd-display-help-about-action (idd-get-action source
  864.                                   destination
  865.                                   idd-actions)
  866.                           source
  867.                           destination)
  868.          (idd-call-action (idd-get-action source destination idd-actions)
  869.                   source
  870.                   destination)))
  871.       (t (message "Wrong event! Exit drag and drop.") nil))
  872.  
  873.     ;; Useful for debugging
  874.     ;;    (setq idd-last-source source)
  875.     ;;    (setq idd-last-destination destination)
  876.  
  877.     )
  878.   (idd-restore-original-pointer-glyphs))
  879.  
  880. (defun idd-help-start-action (event)
  881.   "Used to start the action from the help buffer."
  882.   (interactive "@e")
  883.   (idd-set-point idd-help-destination)
  884.   (idd-call-action (idd-get-action idd-help-source
  885.                    idd-help-destination
  886.                    idd-actions)
  887.            idd-help-source
  888.            idd-help-destination)
  889.   (delete-extent idd-help-start-extent))
  890.  
  891. (if (adapt-xemacsp)
  892.     (progn
  893.  
  894.       (defun idd-make-drag-and-drop-pointer-glyph ()
  895.     "Creates the drag and drop pointer glyph.
  896. You've to rerun this, if you change either the variable
  897. `idd-data-directory' or `idd-mouse-pointer-image'."
  898.     (interactive)
  899.     (let ((mouse-pointer-image (if (and idd-data-directory
  900.                         idd-mouse-pointer-image)
  901.                        (expand-file-name
  902.                     (file-name-nondirectory
  903.                      idd-mouse-pointer-image)
  904.                     idd-data-directory)
  905.                      idd-mouse-pointer-image)))
  906.       (if (and mouse-pointer-image
  907.            (file-exists-p mouse-pointer-image))
  908.           (setq idd-drag-and-drop-pointer-glyph 
  909.             (make-pointer-glyph 
  910.              (vector 'autodetect :data mouse-pointer-image)))
  911.         (setq idd-drag-and-drop-pointer-glyph (make-pointer-glyph))
  912.         (message 
  913.          "Warning: Can't find drag and drop mouse pointer image!"))))
  914.       
  915.       (idd-make-drag-and-drop-pointer-glyph)
  916.       
  917.       ))
  918.  
  919. ;; keymap for help buffer extents
  920. (if (not idd-help-start-action-keymap)
  921.     (progn
  922.       (setq idd-help-start-action-keymap
  923.         (make-sparse-keymap 'idd-help-start-action-keymap))
  924.       (if (adapt-emacs19p)
  925.       (define-key idd-help-start-action-keymap [(mouse-2)]
  926.         'idd-help-start-action)
  927.     (define-key idd-help-start-action-keymap "[(button2)]"
  928.       'idd-help-start-action))))
  929.  
  930. ;; global key bindings
  931. (when idd-global-mouse-keys
  932.   (unless (where-is-internal 'idd-mouse-drag-and-drop global-map t)
  933.     (define-key global-map idd-global-mouse-keys 'idd-mouse-drag-and-drop))
  934.   (unless (where-is-internal 'idd-help-mouse-drag-and-drop global-map t)
  935.     (define-key global-map
  936.       idd-global-help-mouse-keys 'idd-help-mouse-drag-and-drop)))
  937.  
  938.  
  939. (provide 'internal-drag-and-drop)
  940.  
  941. ;;; internal-drag-and-drop ends here
  942.